/*global $ jQuery window LOCK5MAIN*/

$(function() {
    // global objects
    LOCK5MAIN.histogramTypes = ["correlation", "slope"];
    LOCK5MAIN.histograms = {};
    $.each(LOCK5MAIN.histogramTypes, function(i, type) {
        LOCK5MAIN.histograms[type] = $.createHistogram();
        LOCK5MAIN.histograms[type].type = type;
    });
    LOCK5MAIN.activeHistogram = LOCK5MAIN.histograms.correlation;
    LOCK5MAIN.hypothesisTest = true;
    
    /* Methods */
    
    /*
     * LOCK5MAIN.clearGraph Clears everything to do with the LOCK5MAIN
     * randomization graph including data and control settings.
     *
     * Returns true
     */
    LOCK5MAIN.clearGraph = function() {
        $.each(LOCK5MAIN.histograms, function(i, h) {
            h.clear();
        });
        LOCK5MAIN.dotPlot.setBounds({
            xaxis: LOCK5MAIN.ORIGINAL_BOUNDS.xaxis,
            yaxis: LOCK5MAIN.ORIGINAL_BOUNDS.yaxis});
        LOCK5MAIN.dotPlot.clear();
        LOCK5MAIN.hoverPlot("#current-sample-plot", []);
        return true;
    };
    /*
     * hoverGraph: Function that is called when the mouse hovers over a
     * point on the dotplot
     * 
     * parameters: id (String) the css id of the element to plot over
     */
    LOCK5MAIN.hoverGraph = function(id) {
        return function(event, pos, item) {
            if (item) {
                var generatedFrom = item.series.data[item.dataIndex][2];
                LOCK5MAIN.hoverPlot(id, generatedFrom);
            }
        };
    };
    /*
     * LOCK5MAIN.generateNrandomizationSamples: generates statistics from the given
     * points
     *
     * returns: Array of statistics. A statistic is an object containing
     * statistics for each input function type as well as the randomization sample
     * that generated it
     *
     * parameters: point (Array): the original data to sample n (Integer):
     * nubmer of points to generate slopeFunction (function): calculates the
     * slope statistic from the boostrap points correlationFunction (function):
     * calculates the correlation statistic from the boostrap points
     */
    LOCK5MAIN.generateNrandomizationSamples = function(points, n, slopeFunction, correlationFunction) {
        var i, tmp, permutedRange, pointNumber, sampleNumber, randIndex, tmpPoints, statistics = [], slope, correlation, errorsSoFar, MAXERRORS = 100;
        // generate n boostrap samples
        for (sampleNumber = 0; sampleNumber < n; sampleNumber += 1) {
        
            // sometimes due to random chance a random sample
            // generates a degenerate sample resulting in a failure on
            // one of the statistic functions. we allow MAXERRORS of these
            // to occur before we barf. The while loop keeps it going
            // but if a statistic is calculated the loop breaks
            errorsSoFar = 0;
            while (true) {
                try {
                    // generate random permutations
                    permutedRange = [];
                    for (i = 0; i < points.length; i += 1) {
                        permutedRange[i] = i;
                    }
                    for (i = 0; i < points.length; i += 1) {
                        randIndex = Math.floor(Math.random() * points.length);
                        //swap i and randIndex positions
                        tmp = permutedRange[i];
                        permutedRange[i] = permutedRange[randIndex];
                        permutedRange[randIndex] = tmp;
                    }
                    // generate permuted x/y points.
                    tmpPoints = [];
                    for (pointNumber = 0; pointNumber < points.length; pointNumber += 1) {
                        // random sample an index
                        tmpPoints.push([points[pointNumber][0], points[permutedRange[pointNumber]][1]]);
                    }
                    slope = slopeFunction(tmpPoints);
                    correlation = correlationFunction(tmpPoints);
                    statistics.push({
                        slope: slope,
                        correlation: correlation,
                        generatedFrom: tmpPoints
                    });
                    break;
                } 
                catch (err) {
                    errorsSoFar += 1;
                    if (errorsSoFar > MAXERRORS) {
                        throw ("Cannot generate samples, statistic function generates " + err);
                    }
                }
            }
        }
        return statistics;
    };
    /*
     * generateAndPlotrandomizationSample: helper function to generate n additional
     * points and plot the current total.
     *
     * returns: true
     *
     * parameters: point (Array): the original data to sample n (Integer):
     * nubmer of points to generate slopeFunction (function): calculates the
     * slope statistic from the boostrap points correlationFunction (function):
     * calculates the correlation statistic from the boostrap points
     */
    LOCK5MAIN.generateAndPlotrandomizationSample = function(n) { //
        return function() {
        
            // calculate samples
            var statistics = LOCK5MAIN.generateNrandomizationSamples(LOCK5MAIN.ORIGINAL_SAMPLE.data, n, $.calculateRegressionSlope, $.calculateCorrelation);
            
            // add those samples to each histogram
            $.each(LOCK5MAIN.histograms, function(i, h) {
                h.addPoints(statistics);
            });
            // plot the randomization plot with the old and new samples from
            // the current active histogram
            LOCK5MAIN.dotPlot.plot("#dotplot", LOCK5MAIN.activeHistogram.histogramPoints);
            
            // Plot the most recent statistic in the hoverplot so it animates
            // as single stats are added at a time. Doesn't make sense if
            // many points are added, but still keep it for uniformity
            LOCK5MAIN.hoverPlot("#current-sample-plot", statistics[0].generatedFrom);
            
            return true;
        };
    };
    
    /*
     * LOCK5MAIN.initialize(data,domain,range) Initializes both plots based on
     * the 2D set of points passed to it.
     *
     * returns None
     *
     * Parameters data (Array): an N dimensional array made up 2D arrays
     * representing original sample points domain (String): the label of the
     * domain range (String): the label of the range label (String): the label
     * of the data to display to a user
     */
    LOCK5MAIN.initialize = function(data, domain, range, label) {
        var randomizationGraphWidth, randomizationGraphHeight, samplePoints;
        
        LOCK5MAIN.ORIGINAL_SAMPLE.data = data;
        LOCK5MAIN.ORIGINAL_SAMPLE.domain = domain;
        LOCK5MAIN.ORIGINAL_SAMPLE.range = range;
        LOCK5MAIN.ORIGINAL_SAMPLE.label = label;
        
        // LOCK5MAIN.PIXELSPERDOT and physical width of graph determine
        // histogram buckets
        randomizationGraphHeight = parseInt($("#dotplot").height(), 10);
        randomizationGraphWidth = parseInt($("#dotplot").width(), 10);
        
        // randomization some initial points to determine the initial xaxis
        // bounds on graph
        samplePoints = LOCK5MAIN.generateNrandomizationSamples(LOCK5MAIN.ORIGINAL_SAMPLE.data, 1000, $.calculateRegressionSlope, $.calculateCorrelation);
        // set up ranges on each histogram
        $.each(LOCK5MAIN.histograms, function(type, h) {
            (function(array) {
                h.max = $.selection(array, 0);
                h.min = $.selection(array, array.length - 1);
            }($.map(samplePoints, function(sample) {
                return sample[type];
            })));
        });
        LOCK5MAIN.ORIGINAL_BOUNDS = {
            xaxis: {
                //histogram sorts points backwards
                min: LOCK5MAIN.activeHistogram.max,
                max: LOCK5MAIN.activeHistogram.min
            },
            yaxis: {
                min: 0,
                max: Math.floor(parseInt($("#dotplot").height(), 10) / LOCK5MAIN.PIXELSPERDOT)
            }
        };
        LOCK5MAIN.dotPlot.setBounds({
            xaxis: LOCK5MAIN.ORIGINAL_BOUNDS.xaxis,
            yaxis: LOCK5MAIN.ORIGINAL_BOUNDS.yaxis});
        
        $.each(LOCK5MAIN.histograms, function(key, histogram) {
            histogram.k = Math.floor(randomizationGraphWidth / LOCK5MAIN.PIXELSPERDOT);
        });
        LOCK5MAIN.plot("#original-sample-plot", LOCK5MAIN.ORIGINAL_SAMPLE.data, LOCK5MAIN.generateGraphBounds(LOCK5MAIN.ORIGINAL_SAMPLE.data, {
            xaxis: {
                axisLabel: LOCK5MAIN.ORIGINAL_SAMPLE.domain,
                axisLabelUseCanvas: true,
                axisLabelFontSizePixels: 10,
                axisLabelFontFamily: 'Arial'
            },
            yaxis: {
                axisLabel: LOCK5MAIN.ORIGINAL_SAMPLE.range,
                axisLabelUseCanvas: true,
                axisLabelFontSizePixels: 10,
                axisLabelFontFamily: 'Arial'
            }
        }));
        LOCK5MAIN.dotPlot.plot("#dotplot", []);
        
        LOCK5MAIN.plot("#current-sample-plot", [], LOCK5MAIN.generateGraphBounds(LOCK5MAIN.ORIGINAL_SAMPLE.data, {
            xaxis: {
                axisLabel: LOCK5MAIN.ORIGINAL_SAMPLE.domain,
                axisLabelUseCanvas: true,
                axisLabelFontSizePixels: 10,
                axisLabelFontFamily: 'Arial'
            },
            yaxis: {
                axisLabel: LOCK5MAIN.ORIGINAL_SAMPLE.range,
                axisLabelUseCanvas: true,
                axisLabelFontSizePixels: 10,
                axisLabelFontFamily: 'Arial'
            }
        }));
        
        LOCK5MAIN.clearGraph();
        
    };
    
    // set up randomization type pop-up menu
    $(function() {
        var highlight, callback;
        callback = function(histogram) {
            return function() {
                LOCK5MAIN.activeHistogram = histogram;
                LOCK5MAIN.dotPlot.setBounds({
                    xaxis: {
                        //histogram sorts points backwards
                        min: LOCK5MAIN.activeHistogram.max,
                        max: LOCK5MAIN.activeHistogram.min
                    },
                    yaxis: {
                        min: 0,
                        max: Math.floor(parseInt($("#dotplot").height(), 10) / LOCK5MAIN.PIXELSPERDOT)
                    }
                });
                $("#null-hypothesis").html("$\\rho=0$");
                MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
                LOCK5MAIN.dotPlot.plot("#dotplot", LOCK5MAIN.activeHistogram.histogramPoints);
                highlight();
            };
        };
        highlight = function() {
            $("#dotplot").effect("highlight", {
                color: "#eee"
            }, 350);
        };
        $.createPopupMenu($("#randomization-type-button"), [{
            label: "Correlation",
            callback: callback(LOCK5MAIN.histograms.correlation)
        }, {
            label: "Slope",
            callback: callback(LOCK5MAIN.histograms.slope)
        }]);
        
        $("#null-hypothesis").html("$\\rho=0$");
        MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
    });
    /* Initialize the application*/
    $(function() {
        LOCK5MAIN.startup({
            sampleType: "Randomization",
            generateAndPlot: LOCK5MAIN.generateAndPlotrandomizationSample,
            graphAnnotation: function(arrowAnnotationFn, plot) {
                arrowAnnotationFn(plot, 0, "null=0", "#000");
            }
        });
    });
});
